package com.gln.bloger.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.hibernate.Hibernate;

import com.gln.bloger.constant.Constant;
import com.gln.bloger.dao.CategoryDao;
import com.gln.bloger.dao.EntryDao;
import com.gln.bloger.dao.TagDao;
import com.gln.bloger.exception.BlogerException;
import com.gln.bloger.log.Logging;
import com.gln.bloger.pojos.Category;
import com.gln.bloger.pojos.Entry;
import com.gln.bloger.pojos.Tag;
import com.gln.bloger.pojos.Website;
import com.gln.bloger.service.EntryService;
import com.gln.bloger.util.PagingSupport;
import com.gln.bloger.util.StringUtil;
import com.sun.syndication.feed.synd.SyndCategory;
import com.sun.syndication.feed.synd.SyndCategoryImpl;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;

public class EntryServiceImpl implements EntryService {
	private EntryDao entryDao;
	
	private TagDao tagDao;
	
	private List<Entry> cachedRecentEntries;
	
	private List<Date> cachedMonthList;
	
	private CategoryDao categoryDao;
	

	public TagDao getTagDao() {
		return tagDao;
	}

	public void setTagDao(TagDao tagDao) {
		this.tagDao = tagDao;
	}

	public CategoryDao getCategoryDao() {
		return categoryDao;
	}

	public void setCategoryDao(CategoryDao categoryDao) {
		this.categoryDao = categoryDao;
	}

	public EntryDao getEntryDao() {
		return entryDao;
	}

	public void setEntryDao(EntryDao entryDao) {
		this.entryDao = entryDao;
	}

	public PagingSupport<Entry> getAllEntries(Website website)
			throws BlogerException {
		List<Entry> data = getEntryDao().getAllEntries(website);
		PagingSupport<Entry> entries = new PagingSupport<Entry>();
		entries.setData(data);
		return entries;
	}

	public PagingSupport<Entry> getEntries(Website website)
			throws BlogerException {
		List<Entry> data = getEntryDao().getEntries(website);
		for(Entry entry : data){
			Logging.info("force initialize entry's property");
			Hibernate.initialize(entry.getAuthor());
			Hibernate.initialize(entry.getCategory());
			Hibernate.initialize(entry.getTags());
		}
		PagingSupport<Entry> entries = new PagingSupport<Entry>();
		entries.setData(data);
		return entries;
	}

	public Entry getEntry(String entryId) throws BlogerException {
		Entry entry = getEntryDao().get(entryId);
		if(null != entry){
			Hibernate.initialize(entry);
			Hibernate.initialize(entry.getTags());
			Hibernate.initialize(entry.getCategory());
		}
		return entry;
	}

	public Entry getNextEntry(Website website, String id)
			throws BlogerException {
		return this.getEntryDao().getNextEntry(website, id);
	}

	public Entry getPreviousEntry(Website website, String id)
			throws BlogerException {
		return this.getEntryDao().getPreviousEntry(website, id);
	}

	public List<Entry> getRecentEntries(Website website) throws BlogerException {
		if(this.cachedRecentEntries != null && this.cachedRecentEntries.size() != 0){
			Logging.info("load recent entries from app cache");
		}else{
			this.cachedRecentEntries = getEntryDao().getRecentEntries(website, Constant.RECENT_ENTRY_SIZE);
		}
		return this.cachedRecentEntries;
	}

	public List<Entry> getRelatedEntries(Website website, Entry entry, int size)
			throws BlogerException {
		List<Entry> entries = this.getEntryDao().getRelatedEntries(website, entry, size);
		for(Entry en : entries){
			Hibernate.initialize(en);
		}
		return entries;
	}

	public void saveOrUpdateEntry(Entry entry, String cateId, String tagStr)
			throws BlogerException {
		Category category = getCategoryDao().get(cateId);
		
		Category oldCategory = entry.getCategory();
		if(!StringUtil.isBlank(entry.getId())){
			//update
			oldCategory = getCategoryDao().get(oldCategory.getId());
			oldCategory.setCount(oldCategory.getCount()-1);
		}
		
		entry.setCategory(category);
		if(!StringUtil.isBlank(tagStr)){
			Set<Tag> oldTags = entry.getTags();
			List<Tag> tagList = getTagDao().list(entry.getWebsite());
			tagStr = tagStr.replaceAll("，", ",");
			String [] ts = tagStr.split(",");
			
			entry.getTags().clear();
			for(String tName : ts){
				boolean isNew = true;
				tName = tName.trim();
				for(Tag t : tagList){
					if(tName.equalsIgnoreCase(t.getName())){
						isNew = false;
						if (oldTags.contains(t)) {
							oldTags.remove(t);
						} else {
							t.setCount(t.getCount()+1);
						}
						entry.getTags().add(t);
					}
				}
				if(isNew) {
					Tag tag = new Tag();
					tag.setName(tName);
					tag.setCount(1);
					tag.setWebsite(entry.getWebsite());
					//if cascade = update-orphan 
					entry.getTags().add(tag);
				}
			}
			//decrease old tag's count
			for(Tag t: oldTags){
				if(t != null && t.getCount() > 0){
					t.setCount(t.getCount()-1);
				}
				/**
				 * must save tag manually, these are transient object?
				 */
				getTagDao().saveOrUpdate(t);
			}
			//increase tag's count
			for(Tag tag : entry.getTags()){
				tag.setCount(tag.getCount()+1);
				/**
				 *cause set cascade = 'none' on tags declaration  in entry.hbm.xml 
				 *must save tag manually
				 */
				getTagDao().saveOrUpdate(tag);
			}
		}else{
			entry.getTags().clear();
		}
		//increase category's count
		category.setCount(category.getCount()+1);
		//getCategoryDao().saveOrUpdate(category);
		this.getEntryDao().saveOrUpdate(entry);
	}

	public PagingSupport<Entry> getEntriesByCate(Website website, Category cate)
	throws BlogerException {
		List<Entry> data = getEntryDao().getEntries(website, cate);
		for(Entry entry : data){
			Logging.info("force initialize entry's property");
			Hibernate.initialize(entry.getAuthor());
			Hibernate.initialize(entry.getCategory());
			Hibernate.initialize(entry.getTags());
		}
		PagingSupport<Entry> entries = new PagingSupport<Entry>();
		entries.setData(data);
		return entries;
	}

	public PagingSupport<Entry> getEntriesByTag(Website website, String tagId) throws BlogerException {
		List<Entry> data = getEntryDao().getEntries(website, tagId);
		for(Entry entry : data){
			Logging.info("force initialize entry's property");
			Hibernate.initialize(entry.getAuthor());
			Hibernate.initialize(entry.getCategory());
			Hibernate.initialize(entry.getTags());
		}
		PagingSupport<Entry> entries = new PagingSupport<Entry>();
		entries.setData(data);
		return entries;
	}
	
	public PagingSupport<Entry> getEntriesByMonth(Website website, Date month) throws BlogerException {
		List<Entry> data = getEntryDao().getEntriesByMonth(website, month);
		for(Entry entry : data){
			Logging.info("force initialize entry's property");
			Hibernate.initialize(entry.getAuthor());
			Hibernate.initialize(entry.getCategory());
			Hibernate.initialize(entry.getTags());
		}
		PagingSupport<Entry> entries = new PagingSupport<Entry>();
		entries.setData(data);
		return entries;
	}
	
	public SyndFeed getRssFeed(Website website, String feedType, String contextPath) throws BlogerException {
		SyndFeed feed = new SyndFeedImpl();
		feed.setFeedType(feedType);
		List<SyndEntry> entries = new ArrayList<SyndEntry>();
		List<Entry> recentEntries = this.getEntryDao().
		              getRecentEntries(website, Constant.RECENT_ENTRY_SIZE);
		
		for(Entry entry : recentEntries){
			SyndEntry syndEntry = new SyndEntryImpl();
			syndEntry.setTitle(entry.getTitle());
			String link = contextPath+"/"+website.getHandle()+"/entry/show/id"+entry.getId()+"/";
			syndEntry.setLink(link);
			syndEntry.setAuthor(entry.getAuthor().getFullName());
			syndEntry.setPublishedDate(entry.getDateCreated());
			
			List<SyndCategory> categories = new ArrayList<SyndCategory>();
			
			SyndCategory syndCategory = new SyndCategoryImpl();
			syndCategory.setName(entry.getCategory().getName());
			String cateLink = contextPath+"/"+website.getHandle()+ 
			          "/category/list/name/" + entry.getCategory().getName() + "/";
			syndCategory.setTaxonomyUri(cateLink);
			categories.add(syndCategory);
			syndEntry.setCategories(categories);
			
			SyndContent content = new SyndContentImpl();
			content.setType("text/html");
			StringBuilder sb = new StringBuilder();
			sb.append(entry.getSummary())
			  .append("<p>").append("<a href=\"").append(link).append("\">[more..]</a></p>");
			content.setValue(sb.toString());
			syndEntry.setDescription(content);
			entries.add(syndEntry);
		}
		
		feed.setEntries(entries);
		return feed;
	}
	
	
	public List<Date> getArchivedMonthList(Website website)throws BlogerException {
		if(this.cachedMonthList != null && this.cachedMonthList.size() != 0){
			Logging.info("load month list from app cache");
		}else{
		    this.cachedMonthList = getEntryDao().getArchivedMonthList(website);
		}
		return this.cachedMonthList;
	}

	public void removeEntry(String entryId) throws BlogerException {
		Entry entry = getEntryDao().get(entryId);
		this.getEntryDao().remove(entry);
	}

	public void saveOrUpdateEntry(Entry entry) throws BlogerException {
		getEntryDao().saveOrUpdate(entry);
	}

	public void clearCachedMonthList() {
		if(this.cachedMonthList != null){
		    this.cachedMonthList.clear();
		}
	}

	public void clearCachedRecentEntries() {
		if(this.cachedRecentEntries != null){
		    this.cachedRecentEntries.clear();
		}
	}
}
